// synch.cc 
//      Routines for synchronizing threads.  Three kinds of
//      synchronization routines are defined here: semaphores, locks 
//      and condition variables (the implementation of the last two
//      are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      Initialize a semaphore, so that it can be used for synchronization.
//
//      "debugName" is an arbitrary name, useful for debugging.
//      "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore (char *debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      De-allocate semaphore, when no longer needed.  Assume no one
//      is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore ()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
//      Wait until semaphore value > 0, then decrement.  Checking the
//      value and decrementing must be done atomically, so we
//      need to disable interrupts before checking the value.
//
//      Note that Thread::Sleep assumes that interrupts are disabled
//      when it is called.
//----------------------------------------------------------------------

void
Semaphore::P ()
{
    IntStatus oldLevel = interrupt->SetLevel (IntOff);	// disable interrupts

    while (value == 0)
      {				// semaphore not available
	  queue->Append ((void *) currentThread);	// so go to sleep
	  currentThread->Sleep ();
      }
    value--;			// semaphore available, 
    // consume its value

    (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
//      Increment semaphore value, waking up a waiter if necessary.
//      As with P(), this operation must be atomic, so we need to disable
//      interrupts.  Scheduler::ReadyToRun() assumes that threads
//      are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V ()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel (IntOff);

    thread = (Thread *) queue->Remove ();
    if (thread != NULL)		// make thread ready, consuming the V immediately
	scheduler->ReadyToRun (thread);
    value++;
    (void) interrupt->SetLevel (oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock (char *debugName)
{
	name = debugName;	// Name of the Lock
	thread_lock = NULL;	// NULL at initialization
	lock = new Semaphore(debugName, 1);	// Creating Semaphore for locking
}

Lock::~Lock ()
{
	delete lock;
}
void
Lock::Acquire ()
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts
	lock->P();	// Hold the semaphore
	thread_lock = currentThread;	// Assign the new thread for lock
	(void) interrupt->SetLevel(oldLevel); // re-enable interrupts
}
void
Lock::Release ()
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts
	
	ASSERT(thread_lock == currentThread);	// Ensure that the releaser is the lock's owner
	lock->V();	// Free the semaphore
	thread_lock = NULL;	// Set the locker to NULL
	
	(void) interrupt->SetLevel(oldLevel); // re-enable interrupts
}

bool
Lock::isHeldByCurrentThread () {
	bool ret = FALSE;
	IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts
	
	ret = (thread_lock == currentThread);
	
	(void) interrupt->SetLevel(oldLevel); // re-enable interrupts
	
	return ret;
}

Condition::Condition (char *debugName)
{
	name = debugName;	// Name of the condition
	queue = NULL;	// NULL at initialization
	lock = NULL;	// NULL at initialization
	queue = new List();
}

Condition::~Condition ()
{
	//delete lock;
	delete queue;
}
void
Condition::Wait (Lock * conditionLock)
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
	
	ASSERT(conditionLock->isHeldByCurrentThread());	// is the current thread holds this lock
	if(queue->IsEmpty()) {
		lock = conditionLock; // assign the conditionLock to the local lock
	}
	ASSERT(lock == conditionLock); // another pre-condition
	
	queue->Append(currentThread); // adding this thread to the threads's list waiting on condition
	conditionLock->Release(); // release the lock
	currentThread->Sleep(); // goto sleep
	conditionLock->Acquire(); // awaken: re-acquire the lock
	(void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
    // ASSERT (FALSE);
}

void
Condition::Signal (Lock * conditionLock)
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
	Thread *sig_thread;
	
	ASSERT(conditionLock->isHeldByCurrentThread());	// is the current thread holds this lock
	
	if (!queue->IsEmpty()) {	// threads waiting on the condition ?
		ASSERT(lock == conditionLock);	// the called lock == this lock's condition ?
		sig_thread = (Thread *) queue->Remove();	// remove the thread from the waiting queue
		scheduler->ReadyToRun(sig_thread); // wake up the thread
	}
	(void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}
void
Condition::Broadcast (Lock * conditionLock)
{
	Thread *broadThread;
	IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
	ASSERT(conditionLock->isHeldByCurrentThread());	// is the current thread holds this lock
	if(!queue->IsEmpty()) {
		ASSERT(lock == conditionLock);	// the called lock == this lock's condition ?
		while(broadThread = (Thread *)queue->Remove()) { // remove the thread from the waiting queue
			scheduler->ReadyToRun(broadThread); // wake up the thread
		}
	}
	(void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}
